package com.tsunami.utils;

import com.tsunami.common.Constants;
import com.tsunami.enums.Week;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 时间工具类
 *
 * @author lx
 * @date 2021-06-22 11:23
 */
public class TimeUtil {
    private TimeUtil() {
    }

    /**
     * 开始时间标识符
     */
    public static final String TIME_START = "start";
    /**
     * 结束时间标识符
     */
    public static final String TIME_END = "end";
    /**
     * localDate 中间字符
     */
    private static final String LOCAL_DATE_TAG = "T";
    /**
     * LocalDateTime全格式化
     */
    private static final String LOCAL_DATE_TIME_FORMATTER_ALL = "yyyy-MM-dd'T'HH:mm:ss";
    /**
     * LocalDateTime格式化 年月日时分
     */
    private static final String LOCAL_DATE_TIME_FORMATTER_YMD_HM = "yyyy-MM-dd'T'HH:mm";
    /**
     * dateTime全格式化
     */
    private static final String DATE_TIME_FORMATTER_ALL = "yyyy-MM-dd HH:mm:ss";
    /**
     * dateTime全格式化年月日
     */
    public static final String DATE_TIME_FORMATTER_YMD = "yyyy-MM-dd";
    /**
     * dateTime格式化 年月日时分
     */
    private static final String DATE_TIME_FORMATTER_YMD_HM = "yyyy-MM-dd HH:mm";
    /**
     * dateTime格式化 时分
     */
    private static final String DATE_TIME_FORMATTER_HM = "HH:mm";

    /**
     * Date增加分钟
     *
     * @param date 需要操作的时间
     * @param m    增加的分钟数
     * @return 处理后的时间
     */
    public static Date addDateMinut(Date date, int m) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        /*24小时制**/
        cal.add(Calendar.MINUTE, m);
        date = cal.getTime();
        return date;
    }

    /**
     * 转LocalDateTime
     *
     * @param dateTime 时间字符串
     * @return LocalDateTime类型的时间
     */
    public static LocalDateTime stringToJava8Time(String dateTime) {
        if (StringUtils.isNotEmpty(dateTime)) {
            DateTimeFormatter df;
            if (dateTime.contains(LOCAL_DATE_TAG)) {
                df = DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_FORMATTER_ALL);
            } else {
                df = DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER_ALL);
            }
            return LocalDateTime.parse(dateTime, df);
        }
        return null;
    }

    /**
     * 转LocalDateTime
     *
     * @param dateTime 时间字符串
     * @return LocalDateTime类型的时间
     */
    public static LocalDateTime ymdhmToJava8Time(String dateTime) {
        if (StringUtils.isNotEmpty(dateTime)) {
            DateTimeFormatter df;
            if (dateTime.contains(LOCAL_DATE_TAG)) {
                df = DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_FORMATTER_YMD_HM);
            } else {
                df = DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER_YMD_HM);
            }
            return LocalDateTime.parse(dateTime, df);
        }
        return null;
    }

    /**
     * 转LocalDateTime
     *
     * @param dateTime 时间字符串
     * @return LocalDateTime类型的时间
     */
    public static LocalDateTime longToJava8Time(Long dateTime) {
        Date date = new Date(dateTime);
        Instant instant = date.toInstant();
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转字符串
     *
     * @param dateTime     时间
     * @param formatterStr 时间格式 "yyyy-MM-dd HH:mm:ss"
     * @return 时间字符串
     */
    public static String localDateTimeToString(LocalDateTime dateTime, String formatterStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatterStr);
        return dateTime.format(formatter);
    }

    /**
     * LocalDate转LocalDateTime
     *
     * @param date    LocalDate
     * @param timeStr time字符串 ”00:00:00“
     * @return LocalDateTime类型的时间
     */
    public static LocalDateTime localDateToLocalDateTime(LocalDate date, String timeStr) {
        LocalTime localTime = LocalTime.parse(timeStr);
        return date.atTime(localTime);
    }

    /**
     * 获取当前学年
     *
     * @return 学年值
     */
    public static int getSchoolYear() {
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        if (month >= Constants.MONTH_AUGUST) {
            return year;
        }
        return year - 1;
    }

    /**
     * 获取时间段内所有的星期几
     *
     * @param start 开始时间
     * @param end   结束时间
     * @param i     周几 Calendar.THURSDAY周四、Calendar.FRIDAY周五
     * @return 时间列表
     */
    private static ArrayList<Date> getThursdayDatesInRange(Date start, Date end, int i) {
        Calendar calStart = Calendar.getInstance();
        calStart.setTime(start);

        Calendar calEnd = Calendar.getInstance();
        calEnd.setTime(end);

        ArrayList<Date> dates = new ArrayList<>();

        while (!calStart.after(calEnd)) {
            if (calStart.get(Calendar.DAY_OF_WEEK) == i) {
                dates.add(calStart.getTime());
                calStart.add(Calendar.DATE, 7);
            } else {
                calStart.add(Calendar.DATE, 1);
            }
        }

        return dates;
    }

    /**
     * 获取当前时间周开始和结束时间
     *
     * @param date 时间
     * @return 时间map
     */
    public static Map<String, Date> getMcpDayList(Date date) {
        Calendar calendarWeek = Calendar.getInstance();
        calendarWeek.setTime(date);
        calendarWeek.add(Calendar.WEEK_OF_MONTH, 0);
        calendarWeek.setFirstDayOfWeek(Calendar.MONDAY);
        calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        Date startDay = calendarWeek.getTime();
        calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        Date endDay = calendarWeek.getTime();
        Map<String, Date> map = new HashMap<>(3);
        map.put(TIME_START, startDay);
        map.put(TIME_END, endDay);
        return map;
    }

    /**
     * 获取当前时间周开始和结束时间
     *
     * @param date   当前时间
     * @param week   周结束标记
     * @param hour   结束 时
     * @param minute 结束 分
     * @param second 结束 秒
     * @return 时间map
     */
    public static Map<String, Date> getMcpDayList(Date date, String week, int hour, int minute, int second) {
        Calendar calendarWeek = Calendar.getInstance();
        calendarWeek.setTime(date);
        calendarWeek.add(Calendar.WEEK_OF_MONTH, 0);
        calendarWeek.setFirstDayOfWeek(Calendar.MONDAY);
        calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        calendarWeek.set(Calendar.HOUR_OF_DAY, 0);
        calendarWeek.set(Calendar.MINUTE, 0);
        calendarWeek.set(Calendar.SECOND, 0);
        Date startDay = calendarWeek.getTime();
        Date endDay = formatterWeekEndTime(week, hour, minute, second, calendarWeek);
        Map<String, Date> map = new HashMap<>(3);
        map.put(TIME_START, startDay);
        map.put(TIME_END, endDay);
        return map;
    }

    private static Date formatterWeekEndTime(String week, int hour, int minute, int second, Calendar calendarWeek) {
        if (week.equals(Week.MON.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        } else if (week.equals(Week.TUE.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
        } else if (week.equals(Week.WED.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
        } else if (week.equals(Week.THU.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
        } else if (week.equals(Week.FRI.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
        } else if (week.equals(Week.SAT.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
        } else if (week.equals(Week.SUN.getCode())) {
            calendarWeek.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        }
        if (hour > 0) {
            calendarWeek.set(Calendar.HOUR_OF_DAY, hour);
        }
        if (minute > 0) {
            calendarWeek.set(Calendar.MINUTE, minute);
        }
        if (second > 0) {
            calendarWeek.set(Calendar.SECOND, second);
        }
        return calendarWeek.getTime();
    }

    public static Date getDayTime(Date date, String week, int hour, int minute, int second) {
        Calendar calendarWeek = Calendar.getInstance();
        calendarWeek.setTime(date);
        calendarWeek.add(Calendar.WEEK_OF_MONTH, 0);
        return formatterWeekEndTime(week, hour, minute, second, calendarWeek);
    }

    /**
     * 获取当前学年开始和结束时间
     *
     * @return 时间map
     */
    public static Map<String, Date> getMcpDayYear() {
        int year = getSchoolYear();
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, 8);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date startDay = calendar.getTime();
        calendar.set(Calendar.YEAR, year + 1);
        calendar.set(Calendar.MONTH, 7);
        return getStringDateMap(calendar, startDay);
    }

    private static Map<String, Date> getStringDateMap(Calendar calendar, Date startDay) {
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        Date endDay = calendar.getTime();
        Map<String, Date> map = new HashMap<>(3);
        map.put(TIME_START, startDay);
        map.put(TIME_END, endDay);
        return map;
    }

    /**
     * 根据选择的月份返回选择月份的开始时间和结束时间
     *
     * @param month 选择的月份 格式为：yyyy-MM
     * @return 时间map
     */
    public static Map<String, Date> getMcpDayMonth(String month) {
        try {
            Date tempDate = new SimpleDateFormat("yyyy-MM").parse(month);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(tempDate);
            calendar.set(Calendar.DATE, 1);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            Date startDay = calendar.getTime();
            //本月最后一天
            return getStringDateMap(calendar, startDay);
        } catch (ParseException e) {
            e.printStackTrace();
            return Collections.emptyMap();
        }
    }

    /**
     * 获取时间段内的每一天
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 时间列表
     */
    public static List<LocalDate> getDaysInRange(LocalDate startDate, LocalDate endDate) {
        List<LocalDate> daysInRange = new ArrayList<>();
        while (!startDate.isAfter(endDate)) {
            daysInRange.add(startDate);
            startDate = startDate.plusDays(1);
        }
        return daysInRange;
    }

    public static LocalDate getLocalDateByStr(String dateString) {
        if (dateString.length() < Constants.YMD_STRING_LENGTH) {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMATTER_YMD);
            Date date = null;
            try {
                date = sdf.parse(dateString);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            dateString = sdf.format(date);
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER_YMD);
        return LocalDate.parse(dateString, formatter);
    }

    /**
     * 转换时分字符串为LocalTime
     * @param dateString 时分字符串 HH:mm
     * @return localTime
     */
    public static LocalTime getLocalTimeByStr(String dateString) {
        if (dateString.length() < Constants.HM_STRING_LENGTH) {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMATTER_HM);
            Date date = null;
            try {
                date = sdf.parse(dateString);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            dateString = sdf.format(date);
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER_HM);
        return LocalTime.parse(dateString, formatter);
    }

    /**
     * Date转LocalDateTime
     *
     * @param date Date时间
     * @return LocalDateTime格式的时间
     */
    public static LocalDateTime getLocalDateTimeByDate(Date date) {
        Instant instant = date.toInstant();
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转Date
     *
     * @param dateTime LocalDateTime格式的时间
     * @return Date时间
     */
    public static Date getDateByLocalDateTime(LocalDateTime dateTime) {
        ZonedDateTime zonedDateTime = dateTime.atZone(ZoneId.systemDefault());
        Instant instant = zonedDateTime.toInstant();
        return Date.from(instant);
    }
}
